//
// Created by syj on 2024/3/14.
//

#ifndef TRANSMIDDLE_LOGGER_H
#define TRANSMIDDLE_LOGGER_H

#include <mutex>
#include <optional>

#include "middle/log/blogger/blogger.h"
#include "middle/log/common/common.h"
#include "middle/log/logstream.h"
#include "spdlog/logger.h"

namespace log {

/**
 * 日志器封装
 *
 * !!! 系统中所有日志器都应当使用此封装版
 * !!! 否则日志后端初始化动作将多线程不安全
 *
 * 在后端初始化前被打印的日志会被缓冲，直到后端初始化完成后才会被打印
 * 构造过程有加锁，应当避免在多线程环境下频繁创建日志器
 */
    class Logger : public BLogger {
        /** 兼容需继承平台日志实现 */
        BLOGGER_API

    public:
        Logger(std::string const &name = "");
        Logger(Logger const &) = default;
        Logger(Logger &&) = default;
        ~Logger() = default;

        spdlog::logger *operator*() const;
        spdlog::logger *operator->() const;
        spdlog::logger *get() const;

        /** 流输出 */
        LogStream LogCritical() noexcept;
        LogStream LogFatal() noexcept;
        LogStream LogError() noexcept;
        LogStream LogWarn() noexcept;
        LogStream LogInfo() noexcept;
        LogStream LogDebug() noexcept;
        LogStream LogVerbose() noexcept;

    private:
        std::shared_ptr<spdlog::logger> logger_;
    };

/**
 * @brief 默认终端初始化日志接口.
 *
 * @param level 日志等级
 */
    void InitAppLogging(LogLevel level = LogLevel::info);

/**
 * @brief 配置文件初始化日志接口.
 *
 * @param config_file_path 配置文件路径
 */
    void InitAppLogging(const std::string &config_file_path);

/**
 * @brief 通用初始化日志接口.
 *
 * @param name  日志器名字
 * @param level 日志等级
 * @param mode  日志模式
 * @param dirpath   日志文件路径
 * @param filename  日志文件名
 * @param filesize  日志文件大小
 * @param filenum   日志文件数量
 */
    void InitAppLogging(std::string id, LogMode mode,
                        LogLevel level = LogLevel::info,
                        const std::string dirpath = "",
                        const std::string filename = "", std::size_t filesize = 0,
                        std::size_t filenum = 0);

/**
 * @brief 设置日志等级, 若context不为空则设置指定日志器等级
 *
 * @param 等级
 * @param context 日志器名字
 */
    void SetLevel(LogLevel level,
                  std::optional<std::string> const &context = std::nullopt);

/**
 * @brief 获取某个日志器日志等级，若context为空则返回全局日志等级
 *
 * @param context 日志器名称
 * @return 日志等级
 */
    LogLevel GetLevel(std::optional<std::string> const &context = std::nullopt);

/**
 * @brief 获取某个日志器日志等级，若context为空则返回全局日志等级
 *
 * @param context 日志器名称
 * @return 日志等级字符串
 */
    std::string GetLevelStr(
            std::optional<std::string> const &context = std::nullopt);

/**
 * @brief 缓存日志停止输出并刷新异步缓存
 */
    void Stop();

/**
 * @brief 恢复日志输出
 * @param is_replay_backtrace 是否回放日志
 */
    void Resume(bool is_replay_backtrace = true);

/**
 * 打印相互关联的异常信息
 */
    void PrintErrors(std::exception const &errors);

/**
 * 打印相互关联的异常信息
 */
    void PrintErrors(std::vector<std::string> const &errors);

/**
 * 打印相互关联的异常信息
 */
    void PrintErrors(Logger const &logger, std::exception const &errors);

/**
 * 打印相互关联的异常信息
 */
    void PrintErrors(Logger const &logger, std::vector<std::string> const &errors);
}  // namespace log

#endif //TRANSMIDDLE_LOGGER_H
